package com.xj;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.exceptions.BadPasswordException;
import com.lowagie.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

// implementation 'org.apache.pdfbox:pdfbox:2.0.22'
// implementation 'com.itextpdf:itext-asian:5.2.0'
// implementation 'com.github.librepdf:openpdf:1.3.24'

/**
 * @author Ricky
 * @date 2020-07-20
 */
@Slf4j
public class PdfUtil {

    public static int getPdfNumberOfPages(String pdfFilePath) {
        int pageCount;
        try (PdfReader reader = new PdfReader(pdfFilePath)) {
            pageCount = reader.getNumberOfPages();
        } catch (IOException e) {
            throw new RuntimeException("读取Pdf页数失败", e);
        }
        return pageCount;
    }

    public static List<File> pdfToPic(List<File> pdfFiles, File dir) {
        return pdfToPic(pdfFiles, dir, null);
    }

    public static List<File> pdfToPic(List<File> pdfFiles, File dir, Integer dpi) {
        List<File> result = new ArrayList<>();
        for (File pdfFile : pdfFiles) {
            result.addAll(pdfToPic(pdfFile, dir, dpi));
        }
        return result;
    }

    public static List<File> pdfToPic(File pdfFile, File dir) {
        return pdfToPic(pdfFile, dir, null);
    }

    public static List<File> pdfToPic(File pdfFile, File dir, Integer dpi) {
        if (!pdfFile.exists()) {
            throw new RuntimeException("pdf文件不存在");
        }
        if (!dir.exists()) {
            dir.mkdirs();
        }
        List<File> fileList = new ArrayList<>();
        try (PDDocument document = PDDocument.load(pdfFile)) {
            PDFRenderer renderer = new PDFRenderer(document);
            PdfReader reader = new PdfReader(pdfFile.getCanonicalPath());
            int pages = reader.getNumberOfPages();
            for (int i = 0; i < pages; i++) {
                BufferedImage image;
                if (dpi == null) {
                    image = renderer.renderImage(i);
                } else {
                    image = renderer.renderImageWithDPI(i, dpi);
                }
                String baseName = FilenameUtils.getBaseName(pdfFile.getName());
                if (i != 0) {
                    baseName += "_" + i;
                }
                File dstFile = new File(dir, baseName + ".jpg");
                ImageIO.write(image, "jpg", dstFile);
                fileList.add(dstFile);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileList;
    }

    /**
     * pdf拆分
     *
     * @param pdfFile
     * @param dir
     * @return
     * @throws IOException
     */
    public static List<File> splitPdf(File pdfFile, File dir) throws IOException {
        return splitPdf(FileUtils.readFileToByteArray(pdfFile), dir, null, null);
    }

    public static List<File> splitPdf(File pdfFile, File dir, Integer startIndex, Integer endIndex) throws IOException {
        return splitPdf(FileUtils.readFileToByteArray(pdfFile), dir, startIndex, endIndex);
    }

    public static List<File> splitPdf(byte[] pdfBytes, File dir) throws IOException {
        return splitPdf(pdfBytes, dir, null, null);
    }

    /**
     * pdf拆分
     *
     * @param pdfBytes
     * @param dir
     * @param startIndex
     * @param endIndex
     * @return
     * @throws IOException
     */
    public static List<File> splitPdf(byte[] pdfBytes, File dir, Integer startIndex, Integer endIndex) throws IOException {

        if (!dir.exists()) {
            dir.mkdirs();
        }

        try (PDDocument document = PDDocument.load(pdfBytes)) {
            PDPageTree pages = document.getPages();
            int count = pages.getCount();
            if (startIndex == null) {
                startIndex = 0;
            }
            if (endIndex == null) {
                endIndex = count - 1;
            }

            List<File> fileList = new ArrayList<>();
            File singlePdfFile;
            for (int i = startIndex; i <= endIndex; i++) {
                singlePdfFile = new File(dir, i + ".pdf");
                try (PDDocument doc = new PDDocument()) {
                    doc.addPage(document.getPage(i));
                    doc.save(singlePdfFile);
                    fileList.add(singlePdfFile);
                }
            }
            return fileList;
        }
    }

    /**
     * pdf合并
     *
     * @param pdfFileList
     * @param outputFile
     * @throws IOException
     */
    public static void merge(List<File> pdfFileList, File outputFile) throws IOException {
        try (FileOutputStream out = new FileOutputStream(outputFile);
             Document document = new Document()) {
            PdfCopy copy = new PdfCopy(document, out);
            document.open();
            for (File file : pdfFileList) {
                PdfReader pdfReader = new PdfReader(file.getCanonicalPath());
                for (int i = 0; i < pdfReader.getNumberOfPages(); i++) {
                    document.newPage();
                    PdfImportedPage page = copy.getImportedPage(pdfReader, i + 1);
                    copy.addPage(page);
                }
            }
            out.flush();
        }
    }

    /**
     * pdf加密
     *
     * @param pdfFilePath
     * @param password
     * @param deletePdfFile
     * @return
     * @throws IOException
     */
    public static String pdfEncryption(String pdfFilePath, String password, boolean deletePdfFile) throws IOException {
        String outputFilePath = pdfEncryption(pdfFilePath, password);
        return getNewPdfPath(pdfFilePath, deletePdfFile, outputFilePath);
    }

    /**
     * pdf解密
     *
     * @param pdfFilePath
     * @param password
     * @param deletePdfFile
     * @return
     * @throws IOException
     */
    public static String pdfDecryption(String pdfFilePath, String password, boolean deletePdfFile) throws IOException {
        String outputFilePath = pdfDecryption(pdfFilePath, password);
        return getNewPdfPath(pdfFilePath, deletePdfFile, outputFilePath);
    }

    private static String getNewPdfPath(String pdfFilePath, boolean deletePdfFile, String outputFilePath) throws IOException {
        File pdfFile = new File(pdfFilePath);
        File outFile = new File(outputFilePath);
        if (deletePdfFile) {
            pdfFile.delete();
            outFile.renameTo(pdfFile);
            return pdfFile.getCanonicalPath();
        } else {
            return outFile.getCanonicalPath();
        }
    }

    /**
     * pdf加密
     *
     * @param pdfFilePath
     * @param password
     * @return
     * @throws IOException
     */
    public static String pdfEncryption(String pdfFilePath, String password) throws IOException {
        System.out.println("开始加密Pdf");
        long start = System.currentTimeMillis();
        String outputPath = getFileName(pdfFilePath, "-encryption");
        File outputFile = new File(outputPath);
        try (FileInputStream fis = new FileInputStream(new File(pdfFilePath));
             FileOutputStream fos = new FileOutputStream(outputFile)) {
            //            PdfReader.unethicalreading = true;
            PdfReader reader = new PdfReader(fis);
            PdfStamper stamper = new PdfStamper(reader, fos);
            stamper.setEncryption(password.getBytes(), password.getBytes(),
                    PdfWriter.ALLOW_PRINTING, PdfWriter.ENCRYPTION_AES_128);
            stamper.close();
            long end = System.currentTimeMillis();
            System.out.println("pdf加密完成，耗时：" + (end - start));
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
            throw new RuntimeException("pdf加密失败：" + e.getMessage(), e);
        }
        return outputFile.getCanonicalPath();
    }

    /**
     * pdf解密
     *
     * @param pdfFilePath
     * @param password
     * @return
     * @throws IOException
     */
    public static String pdfDecryption(String pdfFilePath, String password) throws IOException {
        System.out.println("开始解密Pdf");
        long start = System.currentTimeMillis();
        String outputPath = getFileName(pdfFilePath, "-decryption");
        File outputFile = new File(outputPath);
        try (FileInputStream fis = new FileInputStream(new File(pdfFilePath));
             FileOutputStream fos = new FileOutputStream(outputFile)) {
            //            PdfReader.unethicalreading = true;
            PdfReader reader = new PdfReader(fis, password.getBytes());
            PdfStamper stamper = new PdfStamper(reader, fos);
            stamper.setEncryption("".getBytes(), "".getBytes(),
                    PdfWriter.ALLOW_PRINTING, PdfWriter.ENCRYPTION_AES_128);
            stamper.close();
            long end = System.currentTimeMillis();
            System.out.println("pdf解密完成，耗时：" + (end - start));
        } catch (BadPasswordException e) {
            outputFile.delete();
            throw new RuntimeException("pdf解密失败：密码错误", e);
        } catch (DocumentException | IOException e) {
            throw new RuntimeException("pdf解密失败：" + e.getMessage(), e);
        }
        return outputFile.getCanonicalPath();
    }

    private static String getFileName(String fileName, String hz) {
        String fullPath = FilenameUtils.getFullPath(fileName);
        String baseName = FilenameUtils.getBaseName(fileName);
        String extension = FilenameUtils.getExtension(fileName);
        return fullPath + baseName + hz + "." + extension;
    }


    public static void main(String[] args) throws IOException {
        //        String password = "11111111";
        //        String pdfPath = "E:/Users/Ricky/Desktop/pdf/2/150.pdf";
        //        //        pdfPath = pdfEncryption(pdfPath, password, true);
        //        pdfDecryption(pdfPath, password, true);
        String filePath = "E:\\Users\\Ricky\\Desktop\\pdf\\0-原始文件pdf.pdf";
        pdfToPic(new File(filePath), new File("E:\\Users\\Ricky\\Desktop\\pdf\\2"), 120);
    }
}
